/* CEMethod.m				 
 *
 * This object controls the data of a beaker (molecules, cameras, groups etc.)
 * It is the main document of BeakerBoy and controls everything from loading to
 * setting up the browser which does most of the other work.
 *
 * For interface-info see the header file. The comments in this file mostly
 * cover only the real implementation details.
 *
 * Written by: 		Thomas Engel
 * Created:    		23.10.1993 (Copyleft)
 * Last modified: 	12.11.1994
 */

#define CURRENT_VERSION 1

#import <CEMethod.h>

#import <misckit/MiscString.h>

@implementation CEMethod

+ initialize
{
	if ( self == [CEMethod class] )
		[CEMethod setVersion:CURRENT_VERSION];

	return self;
}

- init
{
	[super init];
	return [self initFromText:[MiscString newWithString:"- myMethod:sender"]];
	
	// BUG BUG..memory leak !
}

- initFromText:theLine
{
	// We will create a new method that stores the given string
	// representation.
	// The method name WILL be changed to conform to the following
	// example..as far as spaces ar concerned:
	//
	// old: -  ( char*)getStringFor: (aType) string   andMore:(char *) this ;
	// new: - (char *)getStringFor:(aType)string andMore:(char *)this
	
	id	tokens;
	int i;
	id	tempString;
	int	parts;
	
	[super init];
	name = [MiscString new];
	[name takeStringValue:theLine];
	
	// removing the "\" sign should be enough to support basic RTF source
	// properly.
	
	[name replaceEveryOccurrenceOf:";" with:" "];
	[name replaceEveryOccurrenceOf:"\\" with:" "];
	[name trimWhiteSpaces];
	[name squashSpaces];

	// Now charAt:0 _MUST_ be the method type !!
	
	selectorName = [MiscString new];
	tokens = [self methodTokens];
	parts = 0;
	
	for( i=0; i<[tokens count]; i++ )
	{
		tempString = [tokens objectAt:i];
		if( [tempString grep:":"] )
		{
			[selectorName concatenate:tempString];
			parts++;
		}
	}
	// Now if we haven't add even one part...this method has only one. And we
	// have to use at least that one. It has to be the last string because we
	// can't have valid parameters anyway..right...
	
	if( parts == 0 ) [selectorName concatenate:
							[tokens objectAt:[tokens count]-1]];
							
	[[tokens freeObjects] free];
	
	return self;
}

- free
{
	[name free];
	[selectorName free];
	return [super free];
}

- (const char *)name
{
	return [name stringValue];
}

- (const char *)selectorName
{
	return [selectorName stringValue];
}

- methodTokens
{
	// this method returns a list htat contains the single tokens of this
	// method. like: (BOOL), set:, (char *), aString
	// The receiver has to free the method !
	
	int	i;
	id	tokens;
	id	aString;
	BOOL	isCast;
	char	current;
	
	// First let's tokenize according to the braces and spaces.
	// Remember that the method should be clean right now. Only Spaces.
	// And only one at a time.
	// It will be simpler to dangle trought the string by hand...then
	// finding some cool tokenize-regex combination.
	
	tokens = [List new];
	
	isCast = NO;
	aString = [MiscString new];
	
	for( i=2; i<[name length]; i++ )
	{
		// Now lets check the differnt type of tokens...
		// First lets split them at the places where they have 
	
		current = [name charAt:i];
		[aString addChar:current];
		
		// Now if we find a "(" we will start parsing a cast. Until we find a 
		// ")" we will add everything we find.
		
		if( current == '(' )
		{
			isCast = YES;
		}
		else if( current == ')' )
		{
			[tokens addObject:aString];
			aString = [MiscString new];
			isCast = NO;	
		}
		// Finding a ":" means that we will add a selector.
		
		else if( current == ':' )
		{
			[tokens addObject:aString];
			aString = [MiscString new];
		}
		// Now given tha fact that we can only have one Space at the time
		// this must be the end of a parameter is it is not part of a cast.
		// sure..we sould remove the space from the string before adding it.
		
		else if( current == ' ' && 
				 isCast == NO )
		{
			[aString trimSpaces];
			if( [aString emptyString] == NO )
			{
				[tokens addObject:aString];
				aString = [MiscString new];
			}
		}
	}
	
	// If there is something inside the string we will add it to the
	// tokens. This is to be sure that we catch all the parts.
	
	if( [aString emptyString] == NO )
		[tokens addObject:aString];

	return tokens;
}

- (int)numberOfArguments
{
	// returns the number of arguments this method takes.
	
	return [selectorName numOfChar:':'];
}

- (BOOL)isInstanceMethod
{
	if( [name charAt:0] == '+' ) return NO;
	return YES;
}

// dragging handling

- dragImage
{
	if( [self isInstanceMethod] )
		return [NXImage findImageNamed:"InstanceMethod"];
		
	return [NXImage findImageNamed:"ClassMethod"];
}

- (const char *)dragImageTitle
{
	return [self selectorName];
}

- prepareDraggingPboard:aPboard
{
	return [self copyTo:aPboard];
}

- copy
{
	return self;
}

- copyTo:aPasteboard
{
	NXAtom textTypes[5];
	NXStream  *textStream;
	
	textTypes[0] = NXRTFPboardType;
	textTypes[1] = NXAsciiPboardType;
	textTypes[2] = NULL;
	[aPasteboard declareTypes:textTypes num:2 owner:self];

	// Just write the selector...this might be useful for creating references !
	// It is not intended to be absolutely cool in the future.
	// In the future we will place some kind of object ref on the pasteboard
	// and perhaps even the whole source when we do ASCII / RTF drags.
	
	// First lets put down the RTF version..
	
	textStream = NXOpenMemory( NULL, 0, NX_WRITEONLY );
 	NXPrintf( textStream, "{\\rtf0\\ansi{\\fonttbl" );
 	NXPrintf( textStream, "\\f0\\fnil Symbol;" );
 	NXPrintf( textStream, "\\f1\\fnil Times-Roman;" );
 	NXPrintf( textStream, "\\f2\\fnil Times-Bold;" );
	NXPrintf( textStream, "}\n" );
	
	NXPrintf( textStream, "\\f0\\fs28 " );
	NXPrintf( textStream, "-" );
	NXPrintf( textStream, "\\f1 " );
	NXPrintf( textStream, " " );
	NXPrintf( textStream, "\\f2 " );
	NXPrintf( textStream, [self selectorName] );
	NXPrintf( textStream, "\\f1 " );
	NXPrintf( textStream, ", " );
	NXPrintf( textStream, "}" );
	
	[aPasteboard writeType:NXRTFPboardType fromStream:textStream];

	NXCloseMemory( textStream, NX_FREEBUFFER );		
	
	// Now write the ASCII version..
	
	textStream = NXOpenMemory( NULL, 0, NX_WRITEONLY );
	NXPrintf( textStream, "- " );
	NXPrintf( textStream, [self selectorName] );
	NXPrintf( textStream, ", " );
	
	[aPasteboard writeType:NXAsciiPboardType fromStream:textStream];

	NXCloseMemory( textStream, NX_FREEBUFFER );		

	return self;
}

// we are conform to the MiscCompare protocol...actuall all we have is
// a single compare method.

- (int)compare:anObject
{
	// Compares the to selectors...caseSensitive !
	// BUG: We will only compare to other methods...but don't chack for that !	

	return [selectorName cmp:[anObject selectorName]];
}

- (int)compare:anObject ignoreCase:(BOOL)flag 
{ return [self compare:anObject];
}
- (int)compare:anObject length:(unsigned long)length
{ return [self compare:anObject];
}
- (int)compare:anObject length:(unsigned long)length ignoreCase:(BOOL)flag
{ return [self compare:anObject];
}
- (int)compareLiteral:(const char *)literal
{ return 0;
}
- (int)compareLiteral:(const char *)literal ignoreCase:(BOOL)flag
{ return 0;
}
- (int)compareLiteral:(const char *)literal length:(unsigned long)length
{ return 0;
}
- (int)compareLiteral:(const char *)literal length:(unsigned long)length ignoreCase:(BOOL)flag
{ return 0;
}
@end

/*
 * History: 13.01.95 Buh
 *			
 *
 * Bugs: - ...
 */